package com.kexio.dynamic.feature.aspect;

import com.kexio.dynamic.feature.annotation.FeatureToggle;
import com.kexio.dynamic.feature.service.FeatureToggleService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 功能开关AOP切面
 * 
 * 拦截标注了@FeatureToggle注解的方法，
 * 根据功能开关状态决定是否执行
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Aspect
@Component
@Order(2) // 在数据源切面之后执行
public class FeatureToggleAspect {

    private static final Logger log = LoggerFactory.getLogger(FeatureToggleAspect.class);

    private final FeatureToggleService featureToggleService;

    public FeatureToggleAspect(FeatureToggleService featureToggleService) {
        this.featureToggleService = featureToggleService;
    }

    /**
     * @FeatureToggle注解切点
     */
    @Pointcut("@within(com.kexio.dynamic.feature.annotation.FeatureToggle) || " +
              "@annotation(com.kexio.dynamic.feature.annotation.FeatureToggle)")
    public void featureTogglePointcut() {
    }

    /**
     * 功能开关环绕通知
     */
    @Around("featureTogglePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = point.getTarget().getClass();

        // 获取功能开关注解
        FeatureToggle featureToggle = getFeatureToggleAnnotation(method, targetClass);
        if (featureToggle == null) {
            return point.proceed();
        }

        String featureName = featureToggle.value();
        if (!StringUtils.hasText(featureName)) {
            log.warn("功能开关名称为空: {}.{}", targetClass.getSimpleName(), method.getName());
            return point.proceed();
        }

        try {
            // 构建上下文
            Map<String, Object> context = buildContext(point, featureToggle);
            
            // 检查功能开关
            boolean enabled = checkFeatureToggle(featureToggle, context);
            
            if (enabled) {
                // 记录访问日志
                if (featureToggle.logAccess()) {
                    log.info("功能开关访问: {} 已启用, 方法: {}.{}", 
                            featureName, targetClass.getSimpleName(), method.getName());
                }
                
                // 执行目标方法
                return point.proceed();
            } else {
                // 功能禁用处理
                return handleDisabledFeature(featureToggle, method, targetClass);
            }
            
        } catch (Exception e) {
            log.error("功能开关检查异常: {}, 方法: {}.{}", 
                    featureName, targetClass.getSimpleName(), method.getName(), e);
            
            // 异常情况下的处理策略
            return handleException(featureToggle, point, e);
        }
    }

    /**
     * 获取功能开关注解
     */
    private FeatureToggle getFeatureToggleAnnotation(Method method, Class<?> targetClass) {
        // 优先级：方法注解 > 类注解
        FeatureToggle annotation = AnnotationUtils.findAnnotation(method, FeatureToggle.class);
        if (annotation != null) {
            return annotation;
        }
        
        return AnnotationUtils.findAnnotation(targetClass, FeatureToggle.class);
    }

    /**
     * 构建上下文参数
     */
    private Map<String, Object> buildContext(ProceedingJoinPoint point, FeatureToggle featureToggle) {
        Map<String, Object> context = new HashMap<>();
        
        try {
            // 添加方法参数
            Object[] args = point.getArgs();
            if (args != null && args.length > 0) {
                for (int i = 0; i < args.length; i++) {
                    if (args[i] != null) {
                        context.put("arg" + i, args[i]);
                        
                        // 尝试提取用户ID和租户ID
                        if (args[i] instanceof Long && i == 0) {
                            // 假设第一个Long参数可能是用户ID
                            context.put("userId", args[i]);
                        }
                    }
                }
            }
            
            // todo 从用户上下文获取信息（如果可用）
            // Long userId = UserInfoUtils.getCurrentUserId();
            // Long tenantId = UserInfoUtils.getCurrentTenantId();
            // if (userId != null) context.put("userId", userId);
            // if (tenantId != null) context.put("tenantId", tenantId);
            
            // 添加注解配置
            context.put("strategy", featureToggle.strategy());
            context.put("percentage", featureToggle.percentage());
            context.put("userBased", featureToggle.userBased());
            context.put("tenantBased", featureToggle.tenantBased());
            
            if (featureToggle.whitelistUsers().length > 0) {
                context.put("whitelistUsers", Arrays.asList(featureToggle.whitelistUsers()));
            }
            
            if (featureToggle.whitelistTenants().length > 0) {
                context.put("whitelistTenants", Arrays.asList(featureToggle.whitelistTenants()));
            }
            
        } catch (Exception e) {
            log.debug("构建功能开关上下文失败", e);
        }
        
        return context;
    }

    /**
     * 检查功能开关
     */
    private boolean checkFeatureToggle(FeatureToggle featureToggle, Map<String, Object> context) {
        String featureName = featureToggle.value();
        
        // 检查时间范围
        if (!isInTimeRange(featureToggle)) {
            log.debug("功能开关不在有效时间范围内: {}", featureName);
            return false;
        }
        
        // 根据策略检查
        return featureToggleService.checkByStrategy(featureName, featureToggle.strategy(), context);
    }

    /**
     * 检查是否在时间范围内
     */
    private boolean isInTimeRange(FeatureToggle featureToggle) {
        // TODO: 实现基于cron表达式的时间范围检查
        // 这里简化处理，默认返回true
        return true;
    }

    /**
     * 处理功能禁用情况
     */
    private Object handleDisabledFeature(FeatureToggle featureToggle, Method method, Class<?> targetClass) 
            throws Throwable {
        String featureName = featureToggle.value();
        
        log.debug("功能开关已禁用: {}, 方法: {}.{}", 
                featureName, targetClass.getSimpleName(), method.getName());
        
        switch (featureToggle.onDisabled()) {
            case RETURN_NULL:
                return null;
                
            case RETURN_DEFAULT:
                return getDefaultValue(featureToggle, method);
                
            case THROW_EXCEPTION:
                String message = StringUtils.hasText(featureToggle.disabledMessage()) 
                    ? featureToggle.disabledMessage() 
                    : "功能暂时不可用: " + featureName;
                throw new FeatureDisabledException(message);
                
            case LOG_AND_CONTINUE:
                log.warn("功能已禁用但继续执行: {}, 方法: {}.{}", 
                        featureName, targetClass.getSimpleName(), method.getName());
                return null;
                
            case FALLBACK:
                return handleFallback(featureToggle, method, targetClass);
                
            case IGNORE:
                log.debug("忽略功能开关状态，正常执行: {}", featureName);
                return null; // 这里应该调用原方法，但为了简化处理返回null
                
            default:
                return null;
        }
    }

    /**
     * 获取默认返回值
     */
    private Object getDefaultValue(FeatureToggle featureToggle, Method method) {
        String defaultValue = featureToggle.defaultValue();
        if (!StringUtils.hasText(defaultValue)) {
            return null;
        }
        
        Class<?> returnType = method.getReturnType();
        
        try {
            if (returnType == String.class) {
                return defaultValue;
            } else if (returnType == Integer.class || returnType == int.class) {
                return Integer.parseInt(defaultValue);
            } else if (returnType == Long.class || returnType == long.class) {
                return Long.parseLong(defaultValue);
            } else if (returnType == Boolean.class || returnType == boolean.class) {
                return Boolean.parseBoolean(defaultValue);
            } else if (returnType == Double.class || returnType == double.class) {
                return Double.parseDouble(defaultValue);
            }
        } catch (NumberFormatException e) {
            log.warn("默认值类型转换失败: {} -> {}", defaultValue, returnType.getSimpleName());
        }
        
        return null;
    }

    /**
     * 处理降级逻辑
     */
    private Object handleFallback(FeatureToggle featureToggle, Method method, Class<?> targetClass) {
        // TODO: 实现降级方法查找和调用逻辑
        // 可以通过约定的方法名或注解来查找降级方法
        log.debug("功能开关降级处理: {}.{}", targetClass.getSimpleName(), method.getName());
        return null;
    }

    /**
     * 处理异常情况
     */
    private Object handleException(FeatureToggle featureToggle, ProceedingJoinPoint point, Exception e) 
            throws Throwable {
        // 在异常情况下，可以选择继续执行原方法或返回默认值
        if (featureToggle.onDisabled() == FeatureToggle.DisabledStrategy.LOG_AND_CONTINUE ||
            featureToggle.onDisabled() == FeatureToggle.DisabledStrategy.IGNORE) {
            
            log.warn("功能开关检查异常，继续执行原方法: {}", featureToggle.value(), e);
            return point.proceed();
        }
        
        return handleDisabledFeature(featureToggle, 
                ((MethodSignature) point.getSignature()).getMethod(),
                point.getTarget().getClass());
    }

    /**
     * 功能禁用异常
     */
    public static class FeatureDisabledException extends RuntimeException {
        public FeatureDisabledException(String message) {
            super(message);
        }
        
        public FeatureDisabledException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}
